Passed
Push — feature/profile-skills-finaliz... ( 961836...9d523b )
by Tristan
06:45
created

queries.ts ➔ createOrRemove   A

Complexity

Conditions 2

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 5
dl 0
loc 11
rs 10
c 0
b 0
f 0
cc 2
1
/**
2
 * Shortcut function that returns the id attribute of an object.
3
 * @param item
4
 */
5
export function getId<T extends { id: number }>(item: T): number {
6
  return item.id;
7
}
8
9
export function identity<T>(value: T): T {
10
  return value;
11
}
12
13
/**
14
 * Returns true if value is not null or undefined.
15
 * Can be used to filter nulls and undefined values out of an array.
16
 * @param item
17
 */
18
export function notEmpty<T>(value: T | null | undefined): value is T {
19
  return value !== null && value !== undefined;
20
}
21
22
export function stringNotEmpty(value: string | null): value is string {
23
  return notEmpty(value) && value.length > 0;
24
}
25
26
/**
27
 * Returns true if value id null OR undefined.
28
 * @param item
29
 */
30
export function empty<T>(
31
  value: T | null | undefined,
32
): value is null | undefined {
33
  return value === null || value === undefined;
34
}
35
36
/**
37
 * From an array of objects, return the first object with a specific id value.
38
 * @param objs
39
 * @param id
40
 */
41
export function find<T extends { id: number }>(
42
  objs: T[],
43
  id: number,
44
): T | null {
45
  const found = objs.filter((item) => item.id === id);
46
  return found.length > 0 ? found[0] : null;
47
}
48
49
/**
50
 * Return all objects from array with the specified key-value attribute.
51
 * @param objs
52
 * @param prop
53
 * @param value
54
 */
55
export function where<T, K extends keyof T>(
56
  objs: T[],
57
  prop: K,
58
  value: any,
59
): T[] {
60
  return objs.filter((obj) => prop in obj && obj[prop] === value);
61
}
62
63
/**
64
 * Return first object from array with the specified key-value attribute.
65
 * @param objs
66
 * @param prop
67
 * @param value
68
 */
69
export function whereFirst<T, K extends keyof T>(
70
  objs: T[],
71
  prop: K,
72
  value: any,
73
): T {
74
  return where(objs, prop, value)[0];
75
}
76
77
/**
78
 * Map each key-value attribute of an object into an array
79
 * @param object
80
 * @param mapFn
81
 */
82
export function objectMap<A, B>(
83
  object: {
84
    [key: string]: A;
85
  },
86
  mapFn: (key: string, value: A) => B,
87
): B[] {
88
  return Object.keys(object).reduce((result: B[], key: string): B[] => {
89
    result.push(mapFn(key, object[key]));
90
    return result;
91
  }, []);
92
}
93
94
/**
95
 * Return a copy of an object, with each value being passed through a map function.
96
 * Each value's key is unaffected.
97
 * @param object
98
 * @param mapFn
99
 */
100
export function mapObjectValues<A, B>(
101
  object: IndexedObject<A>,
102
  mapFn: (value: A) => B,
103
): IndexedObject<B> {
104
  return Object.entries(object).reduce(
105
    (result: IndexedObject<B>, [key, value]) => {
106
      result[key] = mapFn(value);
107
      return result;
108
    },
109
    {},
110
  );
111
}
112
113
type IndexedObject<T> = {
114
  [key: string]: T;
115
};
116
117
/**
118
 * Maps an array of items into an object, with each transformed into an attribute
119
 * @param items array of objects
120
 * @param keyFn Function that returns a unique key for each object. Typically the getId function.
121
 * @param valFn Function that returns a new value for each object.
122
 */
123
export function mapToObjectTrans<A, B>(
124
  items: A[],
125
  keyFn: (item: A) => string | number,
126
  valFn: (item: A) => B,
127
): IndexedObject<B> {
128
  return items.reduce((result: IndexedObject<B>, item: A): IndexedObject<B> => {
129
    const key = keyFn(item);
130
    result[key] = valFn(item);
131
    return result;
132
  }, {});
133
}
134
135
/**
136
 * Maps an array of items into an object, with each item set as an attribute.
137
 * @param items array of objects
138
 * @param keyFn Function that returns a unique key for each object. Typically the getId function.
139
 */
140
export function mapToObject<T>(
141
  items: T[],
142
  keyFn: (item: T) => string | number,
143
): IndexedObject<T> {
144
  return mapToObjectTrans(items, keyFn, (item): T => item);
145
}
146
147
/**
148
 * Checks if an object has an attribute with a particular key
149
 * @param object
150
 * @param key
151
 */
152
export function hasKey<T>(
153
  object: { [key: string]: T },
154
  key: string | number,
155
): boolean {
156
  return object[key] !== undefined;
157
}
158
159
export function toIdMap<T extends { id: number }>(arr: T[]): Map<number, T> {
160
  return arr.reduce((map, x) => {
161
    map.set(x.id, x);
162
    return map;
163
  }, new Map());
164
}
165
166
/**
167
 * Returns the value at the specified key. If the key is not present, throws an error.
168
 * @param object
169
 * @param key
170
 * @param errorMessage
171
 */
172
export function getOrThrowError<T>(
173
  object: { [key: string]: T },
174
  key: string | number,
175
  errorMessage: string,
176
): T {
177
  if (!hasKey(object, key)) {
178
    throw new Error(errorMessage);
179
  }
180
  return object[key];
181
}
182
183
/** Return a copy of the object with specific property removed */
184
export function deleteProperty<T, K extends keyof T>(
185
  obj: T,
186
  key: K,
187
): Omit<T, K> {
188
  const { [key]: _, ...newObj } = obj;
189
  return newObj;
190
}
191
192
/**
193
 * Iterate through the properties of the object, checking which return true for the filterFunction.
194
 * Return a copy of the object, without the properties that don't pass the filter.
195
 */
196
export function filterObjectProps<T>(
197
  obj: IndexedObject<T>,
198
  filter: (value: T, key: string) => boolean,
199
): IndexedObject<T> {
200
  return Object.entries(obj).reduce(
201
    (newObj: IndexedObject<T>, [key, value]): IndexedObject<T> => {
202
      if (filter(value, key)) {
203
        newObj[key] = value;
204
      }
205
      return newObj;
206
    },
207
    {},
208
  );
209
}
210
211
/** Return a copy of the list with duplicate elements removed */
212
export function uniq<T>(x: T[]): T[] {
213
  return Array.from(new Set(x));
214
}
215
216
export function flatten<T>(x: T[][]): T[] {
217
  const concat = (ls: T[], xs: T[]): T[] => ls.concat(xs);
218
  return x.reduce(concat, []);
219
}
220
221
export function removeDuplicatesById<T extends { id: number }>(
222
  items: T[],
223
): T[] {
224
  interface Accumulator {
225
    contents: T[];
226
    ids: number[];
227
  }
228
  const reducer = (result: Accumulator, next: T): Accumulator => {
229
    if (!result.ids.includes(next.id)) {
230
      result.contents.push(next);
231
      result.ids.push(next.id);
232
    }
233
    return result;
234
  };
235
  return items.reduce(reducer, { contents: [], ids: [] }).contents;
236
}
237
238
/*
239
 * Decrement the number if it above zero, else return 0.
240
 * This helps to avoid some pathological edge cases where pendingCount becomes permanently bugged.
241
 * @param num
242
 */
243
export function decrement(num: number): number {
244
  return num <= 0 ? 0 : num - 1;
245
}
246
247
/**
248
 * If the element already exists in the array, then remove it, and return array.
249
 * Otherwise, if the element does not exist in the array, add it to the array, and return new array.
250
 * @param element
251
 * @param array
252
 */
253
export function addOrRemove<T>(element: T, array: T[]): T[] {
254
  return array.includes(element)
255
    ? array.filter((T) => T !== element)
256
    : [...array, element];
257
}
258